{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ['CUDA_VISIBLE_DEVICES'] = '2'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "sys.path.insert(0, \"/home/husein/parsing/self-attentive-parser/src\")\n",
    "sys.path.append(\"/home/husein/parsing/self-attentive-parser\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "from transformers import AlbertTokenizer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AlbertTokenizer\n",
    "tokenizer = AlbertTokenizer.from_pretrained(\n",
    "    'huseinzol05/bert-base-bahasa-cased',\n",
    "    unk_token = '[UNK]',\n",
    "    pad_token = '[PAD]',\n",
    "    do_lower_case = False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'init_inputs': (),\n",
       " 'init_kwargs': {'unk_token': '[UNK]',\n",
       "  'pad_token': '[PAD]',\n",
       "  'do_lower_case': False,\n",
       "  'vocab_file': '/home/husein/.cache/torch/transformers/0c9fbc56f2a8aa995421aacf12696a100db75b9709be6c10a837750077f21259.44cfb00375f2ad0390cef8e4d6b99043bba75c348ba6805ae44e5a4a5b59ebfa',\n",
       "  'special_tokens_map_file': '/home/husein/.cache/torch/transformers/3537099277b83ccb202b83879ac7c8594c862168b7dfffb15a42881cde2e50c8.1f04d662a14dbb60dcf1073d308fd370f7244f21fee819dd5627fb5afeb761b1',\n",
       "  'full_tokenizer_file': None},\n",
       " 'model_max_length': 1000000000000000019884624838656,\n",
       " 'padding_side': 'right',\n",
       " 'model_input_names': ['token_type_ids', 'attention_mask'],\n",
       " '_bos_token': '[CLS]',\n",
       " '_eos_token': '[SEP]',\n",
       " '_unk_token': '[UNK]',\n",
       " '_sep_token': '[SEP]',\n",
       " '_pad_token': '[PAD]',\n",
       " '_cls_token': '[CLS]',\n",
       " '_mask_token': '[MASK]',\n",
       " '_pad_token_type_id': 0,\n",
       " '_additional_special_tokens': [],\n",
       " 'verbose': True,\n",
       " 'added_tokens_encoder': {},\n",
       " 'added_tokens_decoder': {},\n",
       " 'unique_no_split_tokens': ['[PAD]', '[UNK]', '[CLS]', '[MASK]', '[SEP]'],\n",
       " 'do_lower_case': False,\n",
       " 'remove_space': True,\n",
       " 'keep_accents': False,\n",
       " 'vocab_file': '/home/husein/.cache/torch/transformers/0c9fbc56f2a8aa995421aacf12696a100db75b9709be6c10a837750077f21259.44cfb00375f2ad0390cef8e4d6b99043bba75c348ba6805ae44e5a4a5b59ebfa',\n",
       " 'sp_model': <sentencepiece.SentencePieceProcessor; proxy of <Swig Object of type 'sentencepiece::SentencePieceProcessor *' at 0x7fc484711630> >}"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tokenizer.__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['DecodeIds',\n",
       " 'DecodeIdsAsSerializedProto',\n",
       " 'DecodePieces',\n",
       " 'DecodePiecesAsSerializedProto',\n",
       " 'EncodeAsIds',\n",
       " 'EncodeAsPieces',\n",
       " 'EncodeAsSerializedProto',\n",
       " 'GetPieceSize',\n",
       " 'GetScore',\n",
       " 'IdToPiece',\n",
       " 'IsControl',\n",
       " 'IsUnknown',\n",
       " 'IsUnused',\n",
       " 'Load',\n",
       " 'LoadFromSerializedProto',\n",
       " 'LoadOrDie',\n",
       " 'LoadVocabulary',\n",
       " 'NBestEncodeAsIds',\n",
       " 'NBestEncodeAsPieces',\n",
       " 'NBestEncodeAsSerializedProto',\n",
       " 'PieceToId',\n",
       " 'ResetVocabulary',\n",
       " 'SampleEncodeAsIds',\n",
       " 'SampleEncodeAsPieces',\n",
       " 'SampleEncodeAsSerializedProto',\n",
       " 'SetDecodeExtraOptions',\n",
       " 'SetEncodeExtraOptions',\n",
       " 'SetVocabulary',\n",
       " '__class__',\n",
       " '__del__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattr__',\n",
       " '__getattribute__',\n",
       " '__getitem__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__len__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__swig_destroy__',\n",
       " '__swig_getmethods__',\n",
       " '__swig_setmethods__',\n",
       " '__weakref__',\n",
       " 'bos_id',\n",
       " 'decode_ids',\n",
       " 'decode_ids_as_serialized_proto',\n",
       " 'decode_pieces',\n",
       " 'decode_pieces_as_serialized_proto',\n",
       " 'encode_as_ids',\n",
       " 'encode_as_pieces',\n",
       " 'encode_as_serialized_proto',\n",
       " 'eos_id',\n",
       " 'get_piece_size',\n",
       " 'get_score',\n",
       " 'id_to_piece',\n",
       " 'is_control',\n",
       " 'is_unknown',\n",
       " 'is_unused',\n",
       " 'load',\n",
       " 'load_from_serialized_proto',\n",
       " 'load_vocabulary',\n",
       " 'nbest_encode_as_ids',\n",
       " 'nbest_encode_as_pieces',\n",
       " 'nbest_encode_as_serialized_proto',\n",
       " 'pad_id',\n",
       " 'piece_to_id',\n",
       " 'reset_vocabulary',\n",
       " 'sample_encode_as_ids',\n",
       " 'sample_encode_as_pieces',\n",
       " 'sample_encode_as_serialized_proto',\n",
       " 'set_decode_extra_options',\n",
       " 'set_encode_extra_options',\n",
       " 'set_vocabulary',\n",
       " 'this',\n",
       " 'unk_id']"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tokenizer.sp_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "with open('vocab.json') as fopen:\n",
    "    data = json.load(fopen)\n",
    "    \n",
    "LABEL_VOCAB = data['label']\n",
    "TAG_VOCAB = data['tag']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "with tf.gfile.GFile('export/model.pb', 'rb') as f:\n",
    "    graph_def = tf.GraphDef()\n",
    "    graph_def.ParseFromString(f.read())\n",
    "    with tf.Graph().as_default() as graph:\n",
    "        tf.import_graph_def(graph_def)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "input_ids = graph.get_tensor_by_name('import/input_ids:0')\n",
    "word_end_mask = graph.get_tensor_by_name('import/word_end_mask:0')\n",
    "charts = graph.get_tensor_by_name('import/charts:0')\n",
    "tags = graph.get_tensor_by_name('import/tags:0')\n",
    "sess = tf.InteractiveSession(graph = graph)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "BERT_MAX_LEN = 512\n",
    "import numpy as np\n",
    "from parse_nk import BERT_TOKEN_MAPPING\n",
    "\n",
    "def make_feed_dict_bert(sentences):\n",
    "    all_input_ids = np.zeros((len(sentences), BERT_MAX_LEN), dtype=int)\n",
    "    all_word_end_mask = np.zeros((len(sentences), BERT_MAX_LEN), dtype=int)\n",
    "    \n",
    "\n",
    "    subword_max_len = 0\n",
    "    for snum, sentence in enumerate(sentences):\n",
    "        tokens = []\n",
    "        word_end_mask = []\n",
    "\n",
    "        tokens.append(u\"[CLS]\")\n",
    "        word_end_mask.append(1)\n",
    "\n",
    "        cleaned_words = []\n",
    "        for word in sentence:\n",
    "            word = BERT_TOKEN_MAPPING.get(word, word)\n",
    "            # BERT is pre-trained with a tokenizer that doesn't split off\n",
    "            # n't as its own token\n",
    "            if word == u\"n't\" and cleaned_words:\n",
    "                cleaned_words[-1] = cleaned_words[-1] + u\"n\"\n",
    "                word = u\"'t\"\n",
    "            cleaned_words.append(word)\n",
    "\n",
    "        for word in cleaned_words:\n",
    "            word_tokens = tokenizer.tokenize(word)\n",
    "            if not word_tokens:\n",
    "                # The tokenizer used in conjunction with the parser may not\n",
    "                # align with BERT; in particular spaCy will create separate\n",
    "                # tokens for whitespace when there is more than one space in\n",
    "                # a row, and will sometimes separate out characters of\n",
    "                # unicode category Mn (which BERT strips when do_lower_case\n",
    "                # is enabled). Substituting UNK is not strictly correct, but\n",
    "                # it's better than failing to return a valid parse.\n",
    "                word_tokens = [\"[UNK]\"]\n",
    "            for _ in range(len(word_tokens)):\n",
    "                word_end_mask.append(0)\n",
    "            word_end_mask[-1] = 1\n",
    "            tokens.extend(word_tokens)\n",
    "        tokens.append(u\"[SEP]\")\n",
    "        word_end_mask.append(1)\n",
    "\n",
    "        input_ids = tokenizer.convert_tokens_to_ids(tokens)\n",
    "\n",
    "        subword_max_len = max(subword_max_len, len(input_ids))\n",
    "\n",
    "        all_input_ids[snum, :len(input_ids)] = input_ids\n",
    "        all_word_end_mask[snum, :len(word_end_mask)] = word_end_mask\n",
    "\n",
    "    all_input_ids = all_input_ids[:, :subword_max_len]\n",
    "    all_word_end_mask = all_word_end_mask[:, :subword_max_len]\n",
    "    return all_input_ids, all_word_end_mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[   3,  287,  461, 1524,  598,  454, 3809,    4]]),\n",
       " array([[1, 1, 1, 1, 1, 1, 1, 1]]))"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = 'Saya sedang membaca buku tentang Perlembagaan'.split()\n",
    "sentences = [s]\n",
    "i, m = make_feed_dict_bert(sentences)\n",
    "i, m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[[[ 0.        , -3.4987175 , -2.3440106 , ..., -2.1679118 ,\n",
       "           -2.5054266 , -2.3123686 ],\n",
       "          [ 0.        , -2.3637033 , -3.160329  , ..., -1.5874335 ,\n",
       "           -1.588395  , -1.3189538 ],\n",
       "          [ 0.        , -1.8885597 , -2.9229689 , ..., -2.60825   ,\n",
       "           -1.9523171 , -2.2531438 ],\n",
       "          ...,\n",
       "          [ 0.        , -2.2288191 , -4.8851027 , ..., -2.482474  ,\n",
       "           -2.4275987 , -2.6169772 ],\n",
       "          [ 0.        ,  0.8453851 , -2.7107687 , ..., -2.6852412 ,\n",
       "           -1.9578956 , -2.507674  ],\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ]],\n",
       " \n",
       "         [[ 0.        , -3.1719675 , -1.8720998 , ..., -1.7905697 ,\n",
       "           -1.535727  , -1.6749817 ],\n",
       "          [ 0.        , -3.4987175 , -2.3440106 , ..., -2.1679118 ,\n",
       "           -2.5054266 , -2.3123686 ],\n",
       "          [ 0.        , -2.409241  , -2.758874  , ..., -2.593527  ,\n",
       "           -2.2656188 , -2.733156  ],\n",
       "          ...,\n",
       "          [ 0.        , -2.6629348 , -4.3937173 , ..., -2.536593  ,\n",
       "           -2.7119968 , -2.7588263 ],\n",
       "          [ 0.        , -0.94990903, -2.2303543 , ..., -2.1976318 ,\n",
       "           -1.7706802 , -2.212327  ],\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ]],\n",
       " \n",
       "         [[ 0.        , -3.0991662 , -1.0307213 , ..., -1.6916796 ,\n",
       "           -1.0574661 , -1.2802116 ],\n",
       "          [ 0.        , -3.0320091 , -2.074538  , ..., -1.3896977 ,\n",
       "           -1.3876845 , -1.3535184 ],\n",
       "          [ 0.        , -3.4987175 , -2.3440106 , ..., -2.1679118 ,\n",
       "           -2.5054266 , -2.3123686 ],\n",
       "          ...,\n",
       "          [ 0.        , -2.6911922 , -4.2941165 , ..., -2.1195161 ,\n",
       "           -2.3944566 , -2.2021074 ],\n",
       "          [ 0.        , -0.7363076 , -1.1773055 , ..., -1.8726743 ,\n",
       "           -1.3372058 , -1.4602473 ],\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ]],\n",
       " \n",
       "         ...,\n",
       " \n",
       "         [[ 0.        , -2.6062498 ,  0.09659443, ..., -1.3339132 ,\n",
       "           -0.7631233 , -1.8167241 ],\n",
       "          [ 0.        , -2.5661623 , -0.91195256, ..., -1.4259506 ,\n",
       "           -1.3206745 , -1.8452175 ],\n",
       "          [ 0.        , -2.123061  , -0.6958599 , ..., -1.6506659 ,\n",
       "           -1.379626  , -2.1195457 ],\n",
       "          ...,\n",
       "          [ 0.        , -3.4987175 , -2.3440106 , ..., -2.1679118 ,\n",
       "           -2.5054266 , -2.3123686 ],\n",
       "          [ 0.        , -1.5690712 , -0.43915817, ..., -1.5493542 ,\n",
       "           -1.1114148 , -1.9592192 ],\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ]],\n",
       " \n",
       "         [[ 0.        , -4.321028  , -1.5201201 , ..., -1.6682451 ,\n",
       "           -1.424399  , -1.4457097 ],\n",
       "          [ 0.        , -3.9695292 , -2.5318592 , ..., -1.2821605 ,\n",
       "           -1.4586357 , -1.1726085 ],\n",
       "          [ 0.        , -3.5335658 , -2.1151443 , ..., -1.9844629 ,\n",
       "           -1.768751  , -1.6243324 ],\n",
       "          ...,\n",
       "          [ 0.        , -4.2562337 , -4.652139  , ..., -2.076538  ,\n",
       "           -2.4128742 , -2.0950773 ],\n",
       "          [ 0.        , -3.4987175 , -2.3440106 , ..., -2.1679118 ,\n",
       "           -2.5054266 , -2.3123686 ],\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ]],\n",
       " \n",
       "         [[ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ],\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ],\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ],\n",
       "          ...,\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ],\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ],\n",
       "          [ 0.        ,  0.        ,  0.        , ...,  0.        ,\n",
       "            0.        ,  0.        ]]]], dtype=float32),\n",
       " array([[ 0,  4,  8,  9,  6,  3, 13,  1]]))"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "charts_val, tags_val = sess.run((charts, tags), {input_ids: i, word_end_mask: m})\n",
    "charts_val, tags_val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "for snum, sentence in enumerate(sentences):\n",
    "    chart_size = len(sentence) + 1\n",
    "    chart = charts_val[snum,:chart_size,:chart_size,:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# !wget https://raw.githubusercontent.com/michaeljohns2/self-attentive-parser/michaeljohns2-support-tf2-patch/benepar/chart_decoder.pyx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "import chart_decoder_py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(15.76133,\n",
       " array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5]),\n",
       " array([6, 1, 6, 2, 6, 3, 6, 4, 6, 5, 6]),\n",
       " array([1, 4, 5, 0, 5, 0, 0, 3, 2, 0, 3]))"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chart_decoder_py.decode(chart)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "import nltk\n",
    "from nltk import Tree"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "PTB_TOKEN_ESCAPE = {u\"(\": u\"-LRB-\",\n",
    "    u\")\": u\"-RRB-\",\n",
    "    u\"{\": u\"-LCB-\",\n",
    "    u\"}\": u\"-RCB-\",\n",
    "    u\"[\": u\"-LSB-\",\n",
    "    u\"]\": u\"-RSB-\"}\n",
    "\n",
    "\n",
    "def make_nltk_tree(sentence, tags, score, p_i, p_j, p_label):\n",
    "\n",
    "    # Python 2 doesn't support \"nonlocal\", so wrap idx in a list\n",
    "    idx_cell = [-1]\n",
    "    def make_tree():\n",
    "        idx_cell[0] += 1\n",
    "        idx = idx_cell[0]\n",
    "        i, j, label_idx = p_i[idx], p_j[idx], p_label[idx]\n",
    "        label = LABEL_VOCAB[label_idx]\n",
    "        if (i + 1) >= j:\n",
    "            word = sentence[i]\n",
    "            tag = TAG_VOCAB[tags[i]]\n",
    "            tag = PTB_TOKEN_ESCAPE.get(tag, tag)\n",
    "            word = PTB_TOKEN_ESCAPE.get(word, word)\n",
    "            tree = Tree(tag, [word])\n",
    "            for sublabel in label[::-1]:\n",
    "                tree = Tree(sublabel, [tree])\n",
    "            return [tree]\n",
    "        else:\n",
    "            left_trees = make_tree()\n",
    "            right_trees = make_tree()\n",
    "            children = left_trees + right_trees\n",
    "            if label:\n",
    "                tree = Tree(label[-1], children)\n",
    "                for sublabel in reversed(label[:-1]):\n",
    "                    tree = Tree(sublabel, [tree])\n",
    "                return [tree]\n",
    "            else:\n",
    "                return children\n",
    "\n",
    "    tree = make_tree()[0]\n",
    "    tree.score = score\n",
    "    return tree"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(S\n",
      "  (NP-SBJ (<START> Saya))\n",
      "  (VP\n",
      "    (PRP sedang)\n",
      "    (VP\n",
      "      (MD membaca)\n",
      "      (NP (VB buku))\n",
      "      (PP (NN tentang) (NP (IN Perlembagaan))))))\n"
     ]
    }
   ],
   "source": [
    "tree = make_nltk_tree(s, tags_val[0], *chart_decoder_py.decode(chart))\n",
    "print(str(tree))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def make_str_tree(sentence, tags, score, p_i, p_j, p_label):\n",
    "    idx_cell = [-1]\n",
    "    def make_str():\n",
    "        idx_cell[0] += 1\n",
    "        idx = idx_cell[0]\n",
    "        i, j, label_idx = p_i[idx], p_j[idx], p_label[idx]\n",
    "        label = LABEL_VOCAB[label_idx]\n",
    "        if (i + 1) >= j:\n",
    "            word = sentence[i]\n",
    "            tag = TAG_VOCAB[tags[i]]\n",
    "            tag = PTB_TOKEN_ESCAPE.get(tag, tag)\n",
    "            word = PTB_TOKEN_ESCAPE.get(word, word)\n",
    "            s = u\"({} {})\".format(tag, word)\n",
    "        else:\n",
    "            children = []\n",
    "            while ((idx_cell[0] + 1) < len(p_i)\n",
    "                and i <= p_i[idx_cell[0] + 1]\n",
    "                and p_j[idx_cell[0] + 1] <= j):\n",
    "                children.append(make_str())\n",
    "\n",
    "            s = u\" \".join(children)\n",
    "            \n",
    "        for sublabel in reversed(label):\n",
    "            s = u\"({} {})\".format(sublabel, s)\n",
    "        return s\n",
    "    return make_str()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "make_str_tree(s, tags_val[0], *chart_decoder.decode(chart))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
